package gl.android.xmppandroidclient.server;

import gl.android.util.Lg;
import gl.android.xmppandroidclient.BuildConfig;
import gl.android.xmppandroidclient.common.XmppState;
import gl.android.xmppandroidclient.db.MessagePersistencer;
import gl.android.xmppandroidclient.server.interfaces.XmppStateHandler;

import java.util.HashMap;

import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smackx.ping.PingManager;

import android.content.Context;

public class XmppManger implements ConnectionListener {
	ConnectionConfiguration connectionConfig;
	AccountManager accountManager;
	Connection connection;
	private Context context;
	private XmppConfig mConfig;
	private ChatManager cm;
	private MessagePersistencer mMessagePersistencer;
	private static XmppManger mXmppManger;

	private XmppManger() {

	}

	public synchronized static XmppManger getInstance() {
		if (mXmppManger == null) {
			mXmppManger = new XmppManger();
		}
		return mXmppManger;
	};

	/**
	 * 初始化连接信息,服务器名,账号 密码等 配置参数见 {@link XmppConfig}
	 * 
	 * @param context
	 * @param mConfig
	 * @throws XMPPException
	 */
	public synchronized boolean init(Context context, XmppConfig mConfig)
			throws XMPPException {
		this.context = context;
		if (this.mConfig != null && this.mConfig.equals(mConfig)) {
			return false;
		}
		this.mConfig = mConfig;
		connectionConfig = new ConnectionConfiguration(mConfig.host,
				Integer.parseInt(mConfig.port), mConfig.server);

		connectionConfig.setSecurityMode(SecurityMode.disabled);
		connectionConfig.setSASLAuthenticationEnabled(false);

		// connectionConfig
		// .setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
		// connectionConfig.setSASLAuthenticationEnabled(true);
		// connectionConfig.setTruststorePath("/system/etc/security/cacerts.bks");
		// connectionConfig.setTruststorePassword("changeit");
		// connectionConfig.setTruststoreType("bks");

		connectionConfig.setCompressionEnabled(false);
		connectionConfig.setSendPresence(false);
		connectionConfig.setDebuggerEnabled(BuildConfig.DEBUG);
		try {
			// 允许自动连接
			connectionConfig.setReconnectionAllowed(true);
			Class.forName("org.jivesoftware.smack.ReconnectionManager");
		} catch (Exception e1) {
			e1.printStackTrace();
			connectionConfig.setReconnectionAllowed(false);
		}
		mMessagePersistencer = new MessagePersistencer(context);
		return true;
	}

	public synchronized void login() throws XMPPException {
		mXmppStateHandler.handler(XmppState.CONNECTING, mConfig.account);
		connection = new XMPPConnection(connectionConfig);
		connection.connect();// 开启连接
		connection.addConnectionListener(this);

		// 登录
		connection
				.login(mConfig.account, mConfig.password, mConfig.resouceName);
		// 更改在線狀態
		Presence pre = new Presence(Presence.Type.available);
		connection.sendPacket(pre);
		cm = connection.getChatManager();

		// 获取服务器发送来的任何消息的监听
		cm.addChatListener(XmppChatMangerListener.getInstance(context));

		addPing(connection);
		System.out.println(connection.getUser());
		mXmppStateHandler.handler(XmppState.ONLINE, mConfig.account);
	}

	Thread pingThread;

	private void addPing(Connection connection) {
		final PingManager mPingManager = PingManager.getInstanceFor(connection);
		pingThread = new Thread() {
			public boolean ping = true;

			public void run() {
				try {
					while (ping) {
						mPingManager.pingMyServer();
						sleep(60 * 1000);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
					ping = false;
				}
			};
		};
		pingThread.start();
	}

	HashMap<String, Chat> mHashMap = new HashMap<String, Chat>();

	public void sendmessage(String to, String msgbody) {

		try {
			Chat chat;
			if ((chat = mHashMap.get("to")) == null) {
				chat = cm.createChat(to,
						XmppChatMangerListener.getInstance(context));
				mHashMap.put(to, chat);
			}
			Message message = new Message();
			message.setBody(msgbody);
			message.setTo(to);
			message.setFrom(mConfig.account);
			chat.sendMessage(message);
			mMessagePersistencer.save(message);
		} catch (XMPPException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 处理被踢下线
	 */
	public synchronized void handlelogoutByKick() {
		handleDisconnect();
		mXmppStateHandler.handler(XmppState.KICKOUT, mConfig.account);
		mConfig = null;

	}

	public synchronized void handleDisconnect() {
		mXmppStateHandler.handler(XmppState.OFFLINE, mConfig.account);
		if (connection.isConnected()) {
			Lg.i(this,
					"terminatePersistentConnection()... terminate Connection");
			connection.disconnect();
			Lg.i(this, "terminatePersistentConnection()... had disConnection");
		}

	}

	public void destory() {
		handleDisconnect();
		mHashMap.clear();
		if (pingThread != null) {
			pingThread.interrupt();
		}
		mMessagePersistencer.destory();
		mXmppManger = null;
	}

	/**
	 * 处理重连
	 */
	public synchronized void handleReconnection() {
		mXmppStateHandler.handler(XmppState.CONNECTING, mConfig.account);
		if (connectionConfig.isReconnectionAllowed()) {// 是否开启了自动重连机制
			Lg.i(this, "handleReconnection isReconnectionAllowed()... ");
			return;
		} else {
			try {
				handleDisconnect();
				login();
			} catch (XMPPException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void connectionClosed() {
		Lg.i(this, "connectionClosed()...");
		mXmppStateHandler.handler(XmppState.OFFLINE, mConfig.account);
	}

	@Override
	public void connectionClosedOnError(Exception e) {
		Lg.i(this, "connectionClosedOnError()...");
		e.printStackTrace();
		/** 踢下线 **/
		if (e instanceof XMPPException) {
			XMPPException xmppEx = (XMPPException) e;
			StreamError error = xmppEx.getStreamError();
			Lg.e(this, "connectionClosedOnError:" + "XMPPException");
			if (error != null) {
				String reason = error.getCode();
				if ("conflict".equals(reason)) {
					Lg.e(this, "connectionClosedOnError:" + "conflict");
					mXmppManger.handlelogoutByKick();
					return;
				}
			}
		}
		mXmppManger.handleReconnection();
	}

	@Override
	public void reconnectingIn(int seconds) {
		Lg.i(this, "reconnectingIn()...");
		mXmppStateHandler.handler(XmppState.CONNECTING, mConfig.account);
	}

	@Override
	public void reconnectionFailed(Exception e) {
		Lg.i(this, "reconnectionFailed()...");
		mXmppStateHandler.handler(XmppState.OFFLINE, mConfig.account);
	}

	@Override
	public void reconnectionSuccessful() {
		Lg.i(this, "reconnectionSuccessful()...");
		mXmppStateHandler.handler(XmppState.ONLINE, mConfig.account);
	}

	XmppStateHandler mXmppStateHandler;

	public void setXmppStateHandler(XmppStateHandler mAndroidXmppStateHandler) {
		this.mXmppStateHandler = mAndroidXmppStateHandler;

	}

}
